/**
 *uim.js
 *
 */
var config = require('./config.js').config;



var dateTime = function (t) {
    if (t === undefined) {
        t = new Date().getTime();
    } else if (typeof t == 'string' && ['+', '-'].indexOf(t.substr(0, 1)) > -1 && isFinite(t)) {
        t = new Date().getTime() + (t * 1000);
    } else if (typeof t == 'number') {
        //
    } else {
        throw 't type error.';
    }
    var nowDate = new Date(t);
    var year = nowDate.getFullYear();
    var month = nowDate.getMonth() + 1 < 10 ? "0" + (nowDate.getMonth() + 1) : nowDate.getMonth() + 1;
    var date = nowDate.getDate() < 10 ? "0" + nowDate.getDate() : nowDate.getDate();
    var hour = nowDate.getHours() < 10 ? "0" + nowDate.getHours() : nowDate.getHours();
    var minute = nowDate.getMinutes() < 10 ? "0" + nowDate.getMinutes() : nowDate.getMinutes();
    var second = nowDate.getSeconds() < 10 ? "0" + nowDate.getSeconds() : nowDate.getSeconds();
    return year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second;
};

var md5 = function (data) {
    // var Buffer = require("buffer").Buffer;
    var buf = Buffer.from(data);
    var str = buf.toString("binary");
    var crypto = require("crypto");
    return crypto.createHash("md5").update(str).digest("hex");
};

var db_query = function (sql, call_back) {
    console.log(sql);
    conn_pool.getConnection(function (err, tmp_conn) {
        if (err) {
            console.log('\n\n数据库连接池错误\n\n');
        } else {
            // var x = setTimeout(function(){
            //     // 这里可以设置数据库超时
            //     call_back();
            // }, 2000)
            tmp_conn.query(sql, function (query_err, results, fields) {
                if (query_err) {
                    console.log('\n\n' + sql + '\n\n\nerror：数据库查询出错：' + query_err.messge);
                } else {
                    //回调
                    call_back(results, fields);
                }//释放连接
                tmp_conn.release();
            });
        }
    });
};

var getStrBT = function (startStr, StrS, endStr, c) {
    var lenS, lenE, tmpS, tmpE;
    lenS = startStr.length;
    lenE = endStr.length;
    tmpS = StrS.indexOf(startStr) + lenS;
    if (tmpS == lenS - 1) return '';
    tmpE = StrS.indexOf(endStr, tmpS)
    if (tmpE < 0) return '';
    var l = '', r = '';
    if (c) {
        l = startStr;
        r = endStr;
    }
    return l + StrS.substr(tmpS, tmpE - tmpS) + r;
};

// todo 给db_query加上success和error

var parseJSON = function (str) {
    try {
        return JSON.parse(str);
    } catch (e) {
        return undefined;
    }
}
var isPositiveInteger = function (int_param) {
    return ['number', 'bigint'].includes(typeof int_param) && int_param > 0 && BigInt(int_param) % 1n == 0;
}
// 各版本安全字串加解密函数
var uimSafeStringVersion = {
    '1': {
        encode: function ({ int_time, int_unique, char_last, char_random, int_random }) {
            /*
                time + 8
                unique + 12
                check + 6
            */
            var char_8 = ('0000000' + decimalTo(int_time, 62)).substr(-8); // 将时间戳转成62进制并补位
            var char_12 = ('00000000000' + decimalTo(int_unique, 62)).substr(-12); // 将唯一编号转成62进制并补位
            var char_20 = char_8 + char_12;
            var char_tmp = char_20 + char_last;
            var int_md5_start = int_random % 27; // 32-6+1
            var char_check = md5(char_tmp).substr(int_md5_start, 6); // 从临时变量的md5序列中根据随机位连续取6个字符作为检验串
            var char_md5 = md5(char_last + char_check + char_random);
            char_20 = blurString(char_20, 20, 62, char_md5, 3); // 从md5值中取连续3位来混淆
            return char_20 + char_check;
            // return { result: char_20 + char_check, char_md5, int_md5_start, char_tmp, char_8, char_12, char_20 };
        },
        decode: function ({ char_26, int_random, char_random, char_last }) {
            var char_check = char_26.substr(-6);
            var char_md5 = md5(char_last + char_check + char_random);
            var char_20 = focusString(char_26.substr(0, 20), 62, char_md5, 3);
            var char_8 = toDecimal(char_20.substr(0, 8), 62);
            var char_12 = toDecimal(char_20.substr(-12), 62);

            var char_tmp = char_20 + char_last;
            var int_md5_start = int_random % 27; // 32-6+1
            // 从临时变量的md5序列中根据随机位连续取6个字符作为检验串
            if (char_check != md5(char_tmp).substr(int_md5_start, 6)) {
                throw '校验不通过';
                // return { char_8, char_12, char_20, char_check, char_md5, int_md5_start, char_tmp, check: false, char_26, int_random, char_random, char_last };
            }
            return {
                time: char_8 * 1,
                id: char_12
            };
        }
    }
};
var verifySafeString = function (str_safe, str_key = '') {
    // str_safe 36位字符
    // str_key 密码
    if (typeof str_safe != 'string' || str_safe.length != 36) {
        throw 'verifySafeString';
    }
    // 取得随机字串并解成10进制数值
    var char_random = str_safe.substr(-2);
    var int_random = toDecimal(char_random, 62);
    var char_random_md5 = md5(char_random + 'uim.safeString');

    // 取得随机进制并解成10进制数值
    var char_radix_tmp = str_safe.substr(26, 2);
    char_radix = focusString(char_radix_tmp, 62, char_random_md5, 3); // 解码
    // 取得随机进制并解成10进制数值
    var char_version = str_safe.substr(28, 2);
    char_version = focusString(char_version, 62, char_random_md5, 3); // 解码
    // 取得随机进制并解成10进制数值
    var char_tag = str_safe.substr(30, 4);
    char_tag = focusString(char_tag, 62, char_random_md5, 3); // 解码

    var int_tag_radix = toDecimal(char_radix.charAt(0), 62) * 1 + 1;
    var int_tag = toDecimal(char_tag, int_tag_radix) * 1;

    var int_version_radix = toDecimal(char_radix.charAt(1), 62) * 1 + 1;
    var int_version_value = toDecimal(char_version, int_version_radix);
    var int_version = (3844 + int_version_value - int_random) % 3844; // 与随机位相减取模

    var version_result = uimSafeStringVersion[int_version].decode({
        char_26: str_safe.substr(0, 26),
        int_random,
        char_random,
        char_last: str_safe.substr(26, 8),
    });

    return {
        // int_version_value,
        // int_random, char_random,
        // char_radix, char_radix_tmp, char_tag, char_version, char_random_md5, int_version_radix, int_tag_radix,
        // int_version,
        // int_tag,
        // version_result,
        // v: int_version * 1,
        tag: int_tag,
        time: version_result.time,
        id: version_result.id,
    };

}


var getRandomRadix = function (int_value, max_len, max_radix, str_f) {
    str_f = str_f || default_f;
    // var min_radix = 1; // 最小进制
    var tmp_radix_value; // 临时进制下的数值
    // do {
    //     min_radix++;
    //     tmp_radix_value = decimalTo(int_tag, min_radix);
    // } while (tmp_radix_value.length > 3); // 如果该进制的版本数值超过
    for (var min_radix = 2; min_radix <= str_f.length; min_radix++) {
        tmp_radix_value = decimalTo(int_value, min_radix, str_f);
        if (tmp_radix_value.length <= max_len) {
            break;
        }
    }
    if (min_radix > max_radix) {
        throw '1';
    } else if (min_radix == max_radix) {
        return min_radix;
    } else {
        return randomBetween(min_radix, max_radix);
    }
}

var getRandomRadixValue = function (int_value, max_len, max_radix, str_f) {
    var int_radix = getRandomRadix(int_value, max_len, max_radix, str_f);
    if (!int_radix) {
        throw '2';
    }
    var radix = decimalTo(int_radix - 1, max_radix);
    var value = decimalTo(int_value, int_radix);
    return {
        radix,
        value
    }
}
var blurString = function (str_value, int_len, int_radix, str_md5, int_sub_md5) {
    var arr_char = (new Array(int_len).fill('0').join('') + str_value).substr(-int_len).split('');
    // var tmp = [];
    for (var tmp_i = 0; tmp_i < arr_char.length; tmp_i++) {
        var int_sub_md5_value = parseInt(str_md5.substr(tmp_i, int_sub_md5), 16);// 从md5值中取连续int_sub_md5位转为10进制
        var int_blur_value = toDecimal(arr_char[tmp_i], int_radix) * 1 + int_sub_md5_value; // 把当前位字条转为10进制与md5对应位相加
        arr_char[tmp_i] = decimalTo(int_blur_value % int_radix, int_radix); //取模后转回原进制
        // tmp.push({ int_sub_md5_value, int_blur_value })
    }
    // return { arr_char, tmp };
    return arr_char.join('');
};
var focusString = function (str_value, int_radix, str_md5, int_sub_md5) {
    var arr_char = str_value.split('');
    for (var tmp_i = 0; tmp_i < arr_char.length; tmp_i++) {
        var int_sub_md5_value = parseInt(str_md5.substr(tmp_i, int_sub_md5), 16) % int_radix;// 从md5值中取连续int_sub_md5位转为10进制
        var int_focus_value = toDecimal(arr_char[tmp_i], int_radix) - int_sub_md5_value + int_radix; // 把当前位字条转为10进制与md5对应位相减并加进制数
        arr_char[tmp_i] = decimalTo(int_focus_value % int_radix, int_radix); //取模后转回原进制
    }
    return arr_char.join('');
};
/*
version-return + 26
----------------
char-random + 2
version-radix + 1
version-value + 2
tag-radix + 1
tag-value + 4
*/
var defaultSafeStringUseVersion = 1; // 默认安全字串使用的版本，62进制2位字符最大范围3844，用完这个长度后增加总长度（由36到38或更多，理论上来说是不容易超过的，只要该版本的算法不被泄露就不需要用新版本）
var defaultSafeStringUseKey = 'uim'; // 默认安全字串使用的密码
var createSafeString = function ({ int_unique, int_tag, str_key }) {

    // int_unique 唯一序号整数值 62进制12位字符最大范围322626,67623978,99821055
    if (int_unique !== 0 && (!isPositiveInteger(int_unique) || int_unique > 3226266762397899821055n)) {
        throw '3';
    }

    // int_tag 标签 62进制4位字符最大范围14776336
    int_tag = int_tag || 0;
    if (typeof int_tag != 'number' || int_tag % 1 || int_tag > 14776336) {
        throw '4';
    }

    // int_time 时间戳毫秒 62进制8位字符最大范围218340105584895，到8888/12/2 21:19:44
    // 不允许传入时间，让外人无法猜测算法
    var int_time = new Date().getTime();

    // str_key 密码
    // str_key = str_key || defaultSafeStringUseKey;

    const c3844 = Math.pow(62, 2);

    // 得到随机位（错位）
    var int_random = randomBetween(0, c3844);
    var char_random = ('0' + decimalTo(int_random, 62)).substr(-2);
    var char_random_md5 = md5(char_random + 'uim.safeString');

    // int_version 版本号 62进制2位字符最大范围3844
    var int_version = defaultSafeStringUseVersion;
    var int_version_value = (int_version + int_random) % c3844; // 与随机位相加取模

    var rand_radix_value = {
        int_tag: getRandomRadixValue(int_tag, 4, 62), // 根据int_tag最多只能以62进制占4位来生成随机进制
        int_version: getRandomRadixValue(int_version_value, 2, 62), // 根据int_version_value只以62进制占2位来生成随机进制
    };
    if (!rand_radix_value.int_tag || !rand_radix_value.int_version) {
        throw '5';
    }

    var char_radix_tmp = rand_radix_value.int_tag.radix + rand_radix_value.int_version.radix; // 1 + 1
    char_radix = blurString(char_radix_tmp, 2, 62, char_random_md5, 3);
    var char_version = blurString(rand_radix_value.int_version.value, 2, 62, char_random_md5, 3); // 2
    var char_tag = blurString(rand_radix_value.int_tag.value, 4, 62, char_random_md5, 3); // 4
    var char_last = char_radix + char_version + char_tag;

    var version_return = uimSafeStringVersion[int_version].encode({
        int_time,
        int_unique,
        int_random, char_random,
        char_last
    });
    if (typeof version_return != 'string' || version_return.result != 26) {
        // throw 'version return error.';
    }
    return version_return + char_last + char_random;
    // return {
    //     result, version_return,
    //     int_time, int_unique, int_tag,
    //     int_random, int_version, int_version_value,
    //     char_random, char_radix, char_radix_tmp, char_random_md5, char_version, char_tag, char_last
    // };
};

var randomBetween = function (int_min, int_max) {
    return Math.floor((Math.random() * (int_max - int_min + 1)) + int_min);
}
var default_f = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`!@#$%^&*()-_=+[{]}|;:,<.>/?\'"\\ 	';
// 其他进制转10进制，在js支持的精度内有效 // 支持大数
var toDecimal = function (str_number, int_jz, str_f) {
    str_f = str_f || default_f || '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
    var bl = 1n;// 倍率
    var tmp_z, len_s = str_number.length, tmp_i;
    var d_number = 0n; // 临时总计
    if (str_f.length < int_jz) {
        throw '6';
    }
    // str_f = str_f.substr(0, int_jz);
    for (tmp_i = 1; tmp_i <= len_s; tmp_i++) {
        tmp_z = str_f.indexOf(str_number.substr(-tmp_i, 1)); // 得到当前这一位的10进制值（在数字序列中的位置）
        if (tmp_z == -1) {
            throw '7,' + str_number;
        }
        d_number = d_number + BigInt(tmp_z) * bl;
        bl = bl * BigInt(int_jz);
    }
    return d_number.toString();
}

// 10进制转其他进制，在js支持的精度内有效
var decimalTo = function (str_number, int_jz, str_f) {
    str_f = str_f || default_f || '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
    var int_m, d_number = '';
    if (str_number < 0 || int_jz > str_f.length) {
        throw '7,,,' + int_jz + ',,,' + str_number;
    }
    // str_f = str_f.substr(0, int_jz);
    while (str_number) {
        int_m = BigInt(str_number) % BigInt(int_jz);
        d_number = str_f.charAt(int_m.toString()).toString() + d_number;
        str_number = BigInt(str_number) / BigInt(int_jz);
    }
    return d_number || str_f.charAt(0);
}


// exports = {
//     config,
//     dateTime,
//     md5,
//     db_query,
//     getStrBT,
//     parseJSON,
//     isPositiveInteger,
//     verifySafeString,
//     getRandomRadix,
//     getRandomRadixValue,
//     blurString,
//     focusString,
//     createSafeString,
//     randomBetween,
//     toDecimal,
//     decimalTo
// };

exports.config = config;
exports.dateTime = dateTime;
exports.md5 = md5;
exports.db_query = db_query;
exports.getStrBT = getStrBT;
exports.parseJSON = parseJSON;
exports.isPositiveInteger = isPositiveInteger;
exports.verifySafeString = function (e, tag) {
    try {
        var verify_result = verifySafeString(e);
        if (!verify_result) {
            return false;
        }
        if (tag !== undefined && tag != verify_result.tag) {
            return false;
        }
        return verify_result;
    } catch (error) {
        return false;
    }
};
exports.getRandomRadix = getRandomRadix;
exports.getRandomRadixValue = getRandomRadixValue;
exports.blurString = blurString;
exports.focusString = focusString;
exports.createSafeString = function (e) {
    try {
        return createSafeString(e);
    } catch (error) {
        return false;
    }
};
exports.randomBetween = randomBetween;
exports.toDecimal = toDecimal;
exports.decimalTo = decimalTo;
